package cluster_node;

import cluster_node.leshan_source.handler.RedisRegistrationEventPublisher;
import cluster_node.leshan_source.handler.RedisRequestResponseHandler;
import cluster_node.leshan_source.handler.RedisTokenHandler;
import cluster_node.listener.UploadRegistrationListener;
import cluster_node.models.GlobalVar;
import cluster_node.scheduler.service.UploadScheduleService;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.leshan.core.LwM2m;
import org.eclipse.leshan.core.model.ObjectLoader;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
import org.eclipse.leshan.server.californium.LeshanServer;
import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.eclipse.leshan.server.model.LwM2mModelProvider;
import org.eclipse.leshan.server.model.StaticModelProvider;
import org.eclipse.leshan.server.redis.RedisRegistrationStore;
import org.eclipse.leshan.server.redis.RedisSecurityStore;
import org.quartz.Scheduler;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.util.Pool;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.URI;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

public class ServerClusterNode {

    private static Logger LOG = LoggerFactory.getLogger(ServerClusterNode.class);

    private static Properties configuration;
    static {
        configuration = new Properties();
        InputStream in = Object.class.getResourceAsStream("/configuration.properties");
        try {
            configuration.load(in);
        } catch (IOException e) {
            LOG.info("Fail to load configuration file, use default");
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static String modelsFilePath = "/models/";

    private static String[] modelFileNames = {
            "3303.xml", "Generic Sensor.xml"
    };

    public static void main(String[] args) {
        Options options = new Options();

        options.addOption("lh", "coaphost", true, "Set the local CoAP address.\n  Default: any local address.");
        options.addOption("r", "redis", true, "Set the redis uri.\n Default: localhost");
        options.addOption("tc", "testChannel", true, "Delay Output Channel");
        options.addOption("i", "interval", true, "Request Interval, Default: 30");

        CommandLine cl;
        try {
            cl = new DefaultParser().parse(options, args);
        } catch (ParseException e) {
            System.err.println("Parsing failed.  Reason: " + e.getMessage());
            return;
        }

        String localhost;
        if (cl.hasOption("lh")) {
            localhost = cl.getOptionValue("lh");
        } else {
            localhost = "localhost";
        }

        String redis;
        if (cl.hasOption("r")) {
            redis = "redis://:Hannikodo%40redis.cn@" + cl.getOptionValue("r") + ":6379";
        } else {
            redis = "redis://:Hannikodo%40redis.cn@112.124.24.93:6379";
        }

        int interval;
        if (cl.hasOption("i")) {
            interval = Integer.parseInt(cl.getOptionValue("i"));
        } else {
            interval = 15;
        }
        GlobalVar.interval = interval;

        try {
            createAndStartServer(localhost, redis);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void createAndStartServer(String host, String redis) throws Exception {

        Pool<Jedis> jedis = new JedisPool(new URI(redis));

        LeshanServerBuilder builder = new LeshanServerBuilder();

        builder.setLocalAddress(host,
                Integer.parseInt(configuration.getProperty("localPort")));
        builder.setLocalSecureAddress(host, 5684);

        NetworkConfig coapConfig;
        File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME);
        if (configFile.isFile()) {
            coapConfig = new NetworkConfig();
            coapConfig.load(configFile);
        } else {
            coapConfig = LeshanServerBuilder.createDefaultNetworkConfig();
            coapConfig.store(configFile);
        }
        builder.setCoapConfig(coapConfig);

        DefaultLwM2mNodeDecoder decoder = new DefaultLwM2mNodeDecoder();
        builder.setDecoder(decoder);

        List<ObjectModel> models = ObjectLoader.loadDefault();
        models.addAll(ObjectLoader.loadDdfResources(modelsFilePath, modelFileNames));
        LwM2mModelProvider provider = new StaticModelProvider(models);
        builder.setObjectModelProvider(provider);

        RedisRegistrationStore registrationStore = new RedisRegistrationStore(jedis);
        builder.setRegistrationStore(registrationStore);

        builder.setSecurityStore(new RedisSecurityStore(jedis));

        LeshanServer lwServer = builder.build();

        UploadScheduleService uploadScheduleService = new UploadScheduleService(lwServer, jedis);

        RedisTokenHandler tokenHandler = new RedisTokenHandler(jedis, UUID.randomUUID().toString());
        new RedisRequestResponseHandler(jedis, lwServer, lwServer.getRegistrationService(), tokenHandler,
                lwServer.getObservationService());
        lwServer.getRegistrationService().addListener(tokenHandler);
        lwServer.getRegistrationService().addListener(new RedisRegistrationEventPublisher(jedis));
        lwServer.getRegistrationService().addListener(
                new UploadRegistrationListener(jedis, lwServer, uploadScheduleService));

        lwServer.start();
    }
}
